package com.dp.mallchat.common.user.service.impl;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.dp.mallchat.common.common.domain.vo.resp.ApiResult;
import com.dp.mallchat.common.common.thread.MyUncaughtExceptionHandler;
import com.dp.mallchat.common.user.dao.UserDao;
import com.dp.mallchat.common.user.domain.user.entity.IpDetail;
import com.dp.mallchat.common.user.domain.user.entity.IpInfo;
import com.dp.mallchat.common.user.domain.user.entity.User;
import com.dp.mallchat.common.user.service.IpService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Author: dupeng
 * @CreateTime: 2024-06-11  18:14
 * @Description: ip服务类
 */
@Service
@Slf4j
public class IpServiceImpl implements IpService, DisposableBean {

    @Autowired
    private UserDao userDao;


    private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(500),
            new NamedThreadFactory("ip-service", null, false, new MyUncaughtExceptionHandler())
    );


    /**
     * 解析ip
     *
     * @param uid
     * @return 归属地
     */
    @Override
    public void resolveIp(Long uid) {
        threadPool.execute(() -> {
            User user = userDao.getById(uid);
            if (Objects.isNull(user)) {
                return;
            }
            //是否需要刷新ip归属地
            IpInfo ipInfo = user.getIpInfo();
            if (Objects.isNull(ipInfo)) {
                return;
            }
            String needRefreshIp = ipInfo.isNeedRefreshIp();
            if (StringUtils.isBlank(needRefreshIp)) {
                return;
            }
            //尝试刷新
            IpDetail ipDetail = tryRefreshIp(needRefreshIp);
            //更新用户ip
            if (Objects.nonNull(ipDetail)) {
                ipInfo.updateIpDetail(ipDetail);
                user.setIpInfo(ipInfo);
                userDao.updateById(user);
            } else {
                log.error("get ip detail fail ip:{},uid:{}", needRefreshIp, uid);
            }
        });
    }


    /**
     * 尝试刷新ip
     *
     * @param ip
     * @return
     */
    private IpDetail tryRefreshIp(String ip) {
        //最大三次重试
        for (int i = 0; i < 3; i++) {
            IpDetail ipDetailOrNull = getIpDetailOrNull(ip);
            if (Objects.nonNull(ipDetailOrNull)) {
                return ipDetailOrNull;
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    /**
     * 获取ip信息
     *
     * @param ip
     * @return
     */
    private IpDetail getIpDetailOrNull(String ip) {
        //淘宝免费解析ip
        String body = HttpUtil.get("https://ip.taobao.com/outGetIpInfo?ip=" + ip + "&accessKey=alibaba-inc");
        try {
            ApiResult<IpDetail> result = JSONUtil.toBean(body, new TypeReference<ApiResult<IpDetail>>() {
            }, false);
            if (Objects.nonNull(result.getData())) {
                return result.getData();
            }
        } catch (Exception ignored) {

        }
        return null;
    }

    /**
     * 优雅停机
     *
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        threadPool.shutdown();
        if (threadPool.getQueue().size() <= 0) {
            return;
        }
        //等待30秒并停止
        if (!threadPool.awaitTermination(30, TimeUnit.SECONDS)) {
            //记录日志，是否为error级别并且是已启用状态（error级别实例存在）
            if (!log.isErrorEnabled()) {
                log.error("Timed out while waiting for executor [{}] to terminate", threadPool);
            }
        }
    }

    public static void main(String[] args) {
        IpServiceImpl ipService = new IpServiceImpl();
        for (int i = 0; i < 20; i++) {
            IpDetail ipDetailOrNull = ipService.tryRefreshIp("127.0.0.1");
            log.info("result【" + i + "】===>" + new JSONObject(ipDetailOrNull).toString());
        }

    }
}
